#requires -version 2.0
function New-NaServer {
<# 
	.Synopsis
		Create a new NAServer object
	.Description
		The NaServer object is required to perform any actions against a filer.  
	.Parameter Filer 
		Server (IP, Name, FQN of target Filer)
	.Parameter Credential
		PSCredentials to use when connecting via HTTP/HTTPS
	.Parameter Username
		Username to use when connecting via HTTP/HTTPS
	.Parameter Password
		Password to use when connecting via HTTP/HTTPS
	.Parameter vfiler
		Vfiler name if applicable
	.Parameter NOSSL
		If provided then use HTTP when connecting to the filer.
	.Parameter Version
		If provided then use the provided version.  If none is provided then use 1.0.
	.Example
		New-NaServer -Filer "Toaster1" -Credential (Get-Credential)
        
        Will Connect via HTTPS to Toaster1
    .Example
		New-NaServer -Filer "Toaster1" 
        
        Will connect via RPC using the credentials of the current console to Toaster1.
    .Example
		New-NaServer -Filer "Toaster1" -vFiler "v1_Toaster1" -NOSSL
        
        Will Connect via HTTP to the vFiler v1_Toaster running on Toaster1.
	.Outputs
    	Netapp.Manage.NaServer
	.Link
    	Connect-NaServer
    	Disconnect-NaServer
	#> 
	[CmdletBinding(SupportsShouldProcess=$false,SupportsTransactions=$False,ConfirmImpact="None",DefaultParameterSetName="RPC")]
    Param(
		[Parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true, ParameterSetName="HTTPCred", HelpMessage = "The name of the Netapp Filer to connect to.")]
		[Parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true, ParameterSetName="HTTP", HelpMessage = "The name of the Netapp Filer to connect to.")]
        [Parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true, ParameterSetName="RPC",HelpMessage = "The name of the Netapp Filer to connect to.")]
        [string]
        $Filer,
              
		[Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName="HTTPCred", HelpMessage = "The name of the Netapp vFiler to tunnel to.")]	  
        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName="HTTP", HelpMessage = "The name of the Netapp vFiler to tunnel to.")]
		[Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName="RPC", HelpMessage = "The name of the Netapp vFiler to tunnel to.")]
        [string] 
        $vFiler,
        
        [Parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true,ParameterSetName = "HTTPCred",HelpMessage = "PSCredentials to connect to the filer with")]
        [Management.Automation.PSCredential] 
        $Credential,
		
		[Parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true,ParameterSetName = "HTTP",HelpMessage = "Username to connect to the filer with")]
        [string] 
        $UserName,
		
		[Parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true,ParameterSetName = "HTTP",HelpMessage = "Username to connect to the filer with")]
        [string] 
        $Password,
        
        [Parameter(Mandatory = $false,ParameterSetName = "RPC")]
        [Switch]
        $RPC,
        
        [Parameter(Mandatory = $false,ParameterSetName = "HTTP")]
		[Parameter(Mandatory = $false,ParameterSetName = "HTTPCred")]
        [Switch]
        $NoSSL
    )
    Process{
        #Determine if were going to tunnel to a vFiler or not.
        if (!$vFiler){
            $S = New-Object NetApp.SDK.NaServer($filer, 1,0)
            $ServerName = $filer
        }
        else{
            $S = New-Object NetApp.SDK.NaServer($filer, 1,7)
            $S.SetVfilerTunneling($vFiler)
            $ServerName = ("{0}@{1}" -f $vFiler, $filer)
        }
        
        switch ($PScmdlet.parameterSetName)
        {
            "HTTP"
            {
                #if http then we'll be using username password so set that now
                $s.Style = "LOGIN_PASSWORD"
                $s.SetAdminUser($UserName, $Password)
				break;
            }
			"HTTPCred"
            {
                #if http then we'll be using username password so set that now
                $s.Style = "LOGIN_PASSWORD"
                if ($NoSSL){
                    $s.TransportType = "HTTP"
                }
                else{
                    $s.TransportType = "HTTPS"
                }

                $private:user = $credential.GetNetworkCredential().UserName
                $private:pwd = $credential.GetNetworkCredential().Password
                $s.SetAdminUser($private:user, $private:pwd)
				break;
            }
            "RPC"
            {
                $s.Style = "RPC"
				break;
            }  
        }
        try
        {
            $S.Version=([xml]$s.Invoke("system-get-ontapi-version")).results|%{[double]('{0}.{1}' -f $_."major-version",$_."minor-version")}
            $S.OnTap = $s.Invoke("system-get-version").GetChildContent("version")
        }
        catch [NetApp.Manage.NaAuthException]
        {
            write-warning $_.Exception.message
            $s = $null
        }
        catch [NetApp.Manage.NaApiFailedException]
        {
            if ($_.Exception.GetBaseException().ErrorNo -eq 13011){
                Write-Warning $_.Exception.GetBaseException().message
                $s = $null
            }
            else{
                throw $_
            }
        }
        finally{
            Write-Output $s
        }
    }
}
function Connect-NaServer{
<# 
	.Synopsis
		Create persistent connection to a NetApp Filer
	.Description
		Create persistent connection to a NetApp Filer
        
        This connection is stored in the form of a globaly scoped variable $DefaultNaServer.  
	.Parameter Filer 
		Server (IP, Name, FQN of target Filer)
	.Parameter Credential
		PSCredentials to use when connecting via HTTP/HTTPS
	.Parameter Username
		Username to use when connecting via HTTP/HTTPS
	.Parameter Password
		Password to use when connecting via HTTP/HTTPS
	.Parameter vfiler
		Vfiler name if applicable
	.Parameter NOSSL
		If provided then use HTTP when connecting to the filer.
	.Parameter Version
		If provided then use the provided version.  If none is provided then use 1.0.
	.Example
		Connect-NaServer -Filer "Toaster1" -Credential (Get-Credential)
        
        Will Connect via HTTPS to Toaster1
    .Example
		Connect-NaServer -Filer "Toaster1" 
        
        Will connect via RPC using the credentials of the current console to Toaster1.
    .Example
		Connect-NaServer -Filer "Toaster1" -vFiler "v1_Toaster1" -NOSSL
        
        Will Connect via HTTP to the vFiler v1_Toaster running on Toaster1.
	.Outputs
    	Netapp.Manage.NaServer
	.Link
    	Connect-NaServer
    	Disconnect-NaServer
	#> 
	[CmdletBinding(SupportsShouldProcess=$false,SupportsTransactions=$False,ConfirmImpact="None",DefaultParameterSetName="RPC")]
    param(
		[Parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true, ParameterSetName="HTTPCred", HelpMessage = "The name of the Netapp Filer to connect to.")]
		[Parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true, ParameterSetName="HTTP", HelpMessage = "The name of the Netapp Filer to connect to.")]
        [Parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true, ParameterSetName="RPC",HelpMessage = "The name of the Netapp Filer to connect to.")]
        [string]
        $Filer,
              
		[Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName="HTTPCred", HelpMessage = "The name of the Netapp vFiler to tunnel to.")]	  
        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName="HTTP", HelpMessage = "The name of the Netapp vFiler to tunnel to.")]
		[Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName="RPC", HelpMessage = "The name of the Netapp vFiler to tunnel to.")]
        [string] 
        $vFiler,
        
        [Parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true,ParameterSetName = "HTTPCred",HelpMessage = "PSCredentials to connect to the filer with")]
        [Management.Automation.PSCredential] 
        $Credential,
		
		[Parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true,ParameterSetName = "HTTP",HelpMessage = "Username to connect to the filer with")]
        [string] 
        $UserName,
		
		[Parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true,ParameterSetName = "HTTP",HelpMessage = "Username to connect to the filer with")]
        [string] 
        $Password,
        
        [Parameter(Mandatory = $false,ParameterSetName = "RPC")]
        [switch]
        $RPC,
        
        [Parameter(Mandatory = $false,ParameterSetName = "HTTP")]
		[Parameter(Mandatory = $false,ParameterSetName = "HTTPCred")]
        [switch]
        $NoSSL
    )
    Process{
        #Determine if were going to tunnel to a vFiler or not.
        if (!$vFiler){
            $S = New-Object NetApp.SDK.NaServer($filer, 1,0)
            $ServerName = $filer
        }
        else{
            $S = New-Object NetApp.SDK.NaServer($filer, 1,7)
            $S.SetVfilerTunneling($vFiler)
            $ServerName = ("{0}@{1}" -f $vFiler, $filer)
        }
        
        switch ($PScmdlet.parameterSetName)
        {
            "HTTP"
            {
                #if http then we'll be using username password so set that now
                $s.Style = "LOGIN_PASSWORD"
                $s.SetAdminUser($UserName, $Password)
				break;
            }
			"HTTPCred"
            {
                #if http then we'll be using username password so set that now
                $s.Style = "LOGIN_PASSWORD"
                if ($NoSSL){
                    $s.TransportType = "HTTP"
                }
                else{
                    $s.TransportType = "HTTPS"
                }

                $private:user = $credential.GetNetworkCredential().UserName
                $private:pwd = $credential.GetNetworkCredential().Password
                $s.SetAdminUser($private:user, $private:pwd)
				break;
            }
            "RPC"
            {
                $s.Style = "RPC"
				break;
            }  
        }
        try
        {
            $S.Version=([xml]$s.Invoke("system-get-ontapi-version")).results|%{[double]('{0}.{1}' -f $_."major-version",$_."minor-version")}
            $S.OnTap = $s.Invoke("system-get-version").GetChildContent("version")
        }
        catch [NetApp.Manage.NaAuthException]
        {
            write-warning $_.Exception.message
            $s = $null
        }
        catch [NetApp.Manage.NaApiFailedException]
        {
            if ($_.Exception.GetBaseException().ErrorNo -eq 13011){
                Write-Warning $_.Exception.GetBaseException().message
                $s = $null
            }
            else{
                throw $_
            }
        }
        finally
        {
            New-Variable -Name DefaultNaServer -scope Global -Value $s -Force
        }
    }
}
function Disconnect-NaServer {
	<# 
	.Synopsis
		Destroy an existing persistant NAServer object
	.Description
		Destroy an existing persistant [NaServer] object used to call OnTAPI.  
    .Example
        Disconnect from the existing NaServer 
        PS > Disconnect-NaServer
	.Link
		New-NaServer
    	Connect-NaServer
	#> 
    [CmdletBinding(SupportsShouldProcess=$true,SupportsTransactions=$False,ConfirmImpact="low",DefaultParameterSetName="")] 
    Param()
    Process {
	    if (($pscmdlet.ShouldProcess($global:DefaultNaServer.Server, "Disconnect from Filer"))) {
	        Remove-Variable -name DefaultNaServer -scope Global -force
	    }
    }
}
function Get-NaResult {
	<# 
	.Synopsis
		Depreciated... provided for backward compatability
	.Parameter Request	
       	New-Object NetApp.SDK.NaRequest -ArgumentList @("system-get-version")
    .Parameter Server
    	NaServer to query
	#>  
	[CmdletBinding(SupportsShouldProcess=$True,SupportsTransactions=$False,ConfirmImpact="None",DefaultParameterSetName="")] 
    param (
        [Parameter(Position=0,Mandatory=$TRUE,ValueFromPipeline=$True)]
        [NetApp.SDK.NaRequest]
        $request,
        
        [Parameter(Position=1,HelpMessage="[NAServer]NAserver (New-NAServer)",ValueFromPipeline=$True)]
        [NetApp.SDK.NAServer] 
        $server
    )
    process {
        return ([xml]$server.InvokeElem($request)).results  
    }
}
function Get-NaSystemOnTAPIVersion {
	<# 
	.Synopsis
		Returns the current OnTAPI version of the server.
	.Description
		Returns the current OnTAPI version of the server.
	.Parameter Server
        NaServer to query
    .Example
    	# retrieve the supported ONTAP API version.
        PS > $server = New-NaServer -filer "TOASTER" -credentials (Get-Credential)
        PS > Get-NaSystemOnTAPIVersion -server $server
        1.7
    .Example
        PS > $server = New-NaServer -filer "TOASTER" -credentials (Get-Credential)
        PS > $server.Version = Get-NaSystemOnTAPIVersion -server $server
    .Outputs
    	system.string
	.Notes 
	#> 
	[CmdletBinding(SupportsShouldProcess=$FALSE,SupportsTransactions=$False,ConfirmImpact="None",DefaultParameterSetName="")] 
    param (		
		[Parameter(Position=0,HelpMessage="NAserver (New-NAServer)")]
		[NetApp.SDK.NaServer] 
		$server = (Get-NaServer)
    )
    $request = New-Object Netapp.Manage.NaElement -ArgumentList @("system-get-ontapi-version")
    $result  = ([xml]$server.InvokeElem($request)).results
    return ("{0}.{1}" -f $result."major-version",$result."minor-version")
}
function Get-NaAPI {
    [CmdletBinding(SupportsShouldProcess=$FALSE,SupportsTransactions=$False,ConfirmImpact="None",DefaultParameterSetName="")] 
    Param(
        [Parameter(Position=0)]
		[string] $Name,
        
        [Parameter(Position=1,HelpMessage="NAserver (New-NAServer)")]
		[NetApp.SDK.NaServer] 
		$server = (Get-NaServer)
    )
    Begin {
        $APIList = @()
    }
    Process {
        $NaElement = New-Object Netapp.Manage.NaElement("system-api-list")
        $apis = $Server.InvokeElem($naelement).GetChildByName("apis")
        Foreach ($api in $apis.GetChildren()) {
           $APIList +=  $api.getchildcontent("name")
        }
    }
    End {
        if ($Name){
           return $APIList | Where-Object {$_ -match $name}
        } 
        else{
           return $APIList
        }
    }
}
function Get-NaAPIElements {
    Param(
        [string[]]$API,
        [switch]$Output,
		[NetApp.SDK.NaServer]$server = (Get-NaServer)
    )
    Process{
        $e = New-Object NetApp.Manage.NaElement("api-list")
        Foreach ($A in $API){
            $e.AddNewChild('api-list-info',$A)
        }
        $NaElement = New-Object NetApp.Manage.NaElement("system-api-get-elements")
        $NaElement.AddChildElement($e)
        $apilist = $Server.InvokeElem($naelement).getchildbyname("api-entries")
        Foreach ($A in $apiList.getchildbyname("system-api-entry-info")){
            $Obj = "" | Select API, ElementName, ElementType, Optional, AllowNull 
            $N = $A.GetChildContent("name")
            foreach ($e in $a.getchildbyname("api-elements").getchildren()){
                $obj.API = $N
                $obj.ElementName = $e.getchildcontent('name')
                
                switch ($e.getchildcontent('type')) 
                {
                    "string"  {$obj.ElementType = "String"}
                    "integer" {$obj.ElementType = "int"}
                    "boolean" {$obj.ElementType = "Bool"}
                    Default   {
                        $obj.ElementType = $_
                        $objElem = $true
                        }
                }    
                
                IF (!$e.getchildbyname('is-optional')){
                   $Obj.Optional = $False
                } 
                Else{
                   $Obj.Optional = $true
                }
                
				IF (!$e.getchildbyname('is-output')){
                   $objOutput = $False
                } 
                Else{
                   $objOutput = $true
                }
                
				IF (!$e.getchildbyname('is-optional')){
                   $Obj.Optional = $False
                } 
                Else{
                   $Obj.Optional = $true
                }
                
				IF (
                    (!$e.getchildbyname('is-nonempty')) -or `
                    $e.GetChildContent('is-nonempty') -match "false"
                   ) {
                   $Obj.AllowNull = $True
                } 
                Else{
                   $Obj.AllowNull = $False
                }
                
                Switch ($objOutput) 
                {
                    {($Output -eq $true) -and ($_ -eq $true)}    {write-output $obj}
                    {($Output -eq $false) -and ($_ -eq $false)}  {write-output $obj}
                }
            }
        }
    }
}
function Get-NaType {
    Param(
        [NetApp.SDK.NaServer]$Server=$DefaultNaServer,
        [string]$Type
    )
    process{
        $NaElement = New-Object NetApp.Manage.NaElement("system-api-list-types")
        $results = $Server.InvokeElem($NaElement).GetChildByName("type-entries").getchildren()  
        If ($type){
            $results = $results | ?{$_.GetChildContent("name") -eq $type}
        }
        $results.GetChildByName("type-elements").getchildren() | Select @{
                    n='name'
                    E={$_.GetChildContent("name")}
                }, @{
                    n='type'
                    E={$_.GetChildContent("type")}
                }, @{
                    n='optional'
                    E={$_.GetChildContent("is-optional")}
                }
 
    }
    
}

Function ConvertFrom-NaBool {
    <# 
	.Synopsis
		Converts the string bool returned from the Na API into a proper bool type.
	.Description
		Converts the string bool returned from the Na API into a proper bool type.
	.Parameter bool
       	string bool returned from the ONTAPI API.
    .Example
        convert the string false to $FALSE
   		PS > ConvertFrom-NaBool "false"
    .Outputs
    	system.bool
    .Link
    	ConvertFrom-NaTime
    	ConvertTo-NaTime
	#> 
	[CmdletBinding(SupportsShouldProcess=$FALSE,SupportsTransactions=$False,ConfirmImpact="None",DefaultParameterSetName="")] 
    param(
        [Parameter(Position=0,ValueFromPipeline=$True,HelpMessage="string bool returned from the Na API.")]
		[string[]]
        $bool
    )
    process {
        switch ($bool) {
            "false" {return $FALSE}
            "true"  {return $TRUE}
            default {return $null}
        }
    }
}
Function ConvertFrom-NaTime {
	<# 
	.Synopsis
		Converts the time object returned by the na API into [DateTime] objects
	.Description
		Converts the time object returned by the na API into [DateTime] objects
        Filers record time in the format of seconds since Jan/1/1970.  Some portions 
        of the SDK will return GMT regaurdless of any reginalizaion settings.
	.Parameter Time
       	Seconds since 1/1/1970
    .Parameter UTC
    	Specifies that the time needs to be converted from UTC/GMT.
    .Example
    	convert 1230163200 from seconds since 1/1/1970 to [DateTime].
    	ConvertFrom-NaTime 1230163200
    .Outputs
    	system.DateTime
    .Link
    	ConvertFrom-NaBool
    	ConvertTo-NaTime
	#> 
	[CmdletBinding(SupportsShouldProcess=$FALSE,SupportsTransactions=$False,ConfirmImpact="None",DefaultParameterSetName="")] 
    param(
        [Parameter(Position=0,ValueFromPipeline=$True,HelpMessage="Seconds since 1/1/1970")]
        [int64]
        $Time, 
        
        [Parameter()]
        [switch]
        $UTC
    )
    process {
        if ($UTC) {
            return (New-Object Datetime -ArgumentList @(1970,1,1,0,0,0,[DateTimeKind]1)).AddSeconds($time).ToLocalTime()
        } else {
            return (New-Object Datetime -ArgumentList @(1970,1,1,0,0,0)).AddSeconds($time).ToLocalTime()
        }
    }
}
Function ConvertTo-NaTime {
	<# 
	.Synopsis
		Converts a proper [DateTime] object to the time formate used by filers.
	.Description
		Converts a proper [DateTime] object to the time formate used by filers.
        Filers record time in the format of seconds since Jan/1/1970.  Some portions 
        of the SDK will return GMT regaurdless of any localization settings.  In 
        these instances you will need to specify the -UTC switch.
	.Parameter Time
       	Datetime to be converted
    .Example
    	#convert 12/25/2008 to the correct format for the filers.
	   	PS > ConvertTo-NaTime [DateTime]'12/25/2008'
    .Outputs
    	system.string
    .Link
    	ConvertFrom-NaBool
    	ConvertFrom-NaTime
	#> 
	[CmdletBinding(SupportsShouldProcess=$FALSE,SupportsTransactions=$False,ConfirmImpact="None",DefaultParameterSetName="")] 
    param(
        [Parameter(Mandatory=$TRUE,Position=0,ValueFromPipeline=$True,	        HelpMessage="Datetime to be converted")]
        [DateTime]
        $Time 
    )
    process {
         return [Int64]([math]::ROUND((($Time.ToUniversalTime()).subtract((New-Object Datetime -ArgumentList @(1970,1,1,0,0,0)).ToLocalTime())).TotalSeconds))
    }
}
function Get-NaServer {
	<# 
	.Synopsis
		Get's the current Filer Connection if none is found then create one.
	.Example
        Get-NaServer
	.Link
		New-NaServer
    	Connect-NaServer
        Disconnect-NaServer 
	#> 
    [CmdletBinding(SupportsShouldProcess=$false,SupportsTransactions=$False,ConfirmImpact="low",DefaultParameterSetName="")] 
    param()
    Process {
	    if (!$global:DefaultNaServer){
            write-warning "@ Target Filer could not be found.
			To avoid this error: 
            1.) Pass the -server parameter
            2.) create a persistent connection with the Connect-NaServer cmdlet.
              
         We will now attempt to create a one time connection for you.
@"
	        Return New-NaServer
	    }
        else {
            return $global:DefaultNaServer
        }
    }
}
